﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoreRay
{
    /// <summary>
    /// The Mess class defines a 3D object composed of Triangles. A mesh have a material associated to it.
    /// </summary>
    public class Mesh : GeometryObject
    {
        private SpatialStructure spatialStructure;
        private List<Triangle> triangleList;

        public Mesh()
        {
            this.triangleList = new List<Triangle>();
        }

        public GeometryBuffer GeometryBuffer
        {
            get;
            set;
        }

        public bool IsCommited
        {
            get;
            private set;
        }

        public Material Material
        {
            get;
            protected set;
        }

        public void Commit()
        {
            // Init triangle list
            this.triangleList.Clear();

            for (int i = 0; i < (this.GeometryBuffer.IndexBuffer.Count / 3); i++)
            {
                this.triangleList.Add(new Triangle(this.GeometryBuffer, i));
            }

            // Compute bounding box
            this.BoundingBox = new BoundingBox();

            for (int i = 0; i < this.GeometryBuffer.VertexBuffer.Count; i++)
            {
                Vector3 position = this.GeometryBuffer.VertexBuffer.GetVertexElement<Vector3>(i, "Position");
                this.BoundingBox.Add(position);
            }

            // Init spatial structure
            this.spatialStructure = new GridSpatial(false);
            //this.spatialStructure = new BvhSpatial(false, 4, 100);
            //this.spatialStructure = new ReferenceSpatial();
            this.spatialStructure.Construct(this.triangleList.Select(item => item as GeometryObject).ToList());

            // Set IsCommited flag
            this.IsCommited = true;
        }

        public override Intersection Intersect(Ray ray)
        {
            if (this.IsCommited == false)
            {
                throw new InvalidOperationException("Mesh must be commited before an intersect operation.");
            }

            Intersection intersection = this.spatialStructure.Intersect(ray * this.InversedWorldMatrix);

            if (intersection != null)
            {
                intersection.DifferentialGeometry.TransformToWorldSpace(this.WorldMatrix, this.InversedWorldMatrix);
                return intersection;
            }

            return null;
        }

        public override bool IntersectPosition(Ray ray)
        {
            if (this.IsCommited == false)
            {
                throw new InvalidOperationException("Mesh must be commited before an intersect operation.");
            }

            return this.spatialStructure.IntersectPosition(ray * this.InversedWorldMatrix);
        }
    }
}
